
#include <iostream>
#include <string>

#include <fs.hpp>
#include <xbase_utility.hpp>

#include <../tablet_server/sstable.hpp>

using namespace std;
using namespace xbase;

const string sample_data("abcdefg1234567890_");

void write_sstable_file(string filename,
			int row_size,
			int column_size,
			int value_size,
			int block_size,
			int kv_number,
			int code);
void write_raw_file(string filename, int block_size, int block_number);

void fill_raw_buffer(char *buf, const size_t size);
void fill_sstable_buffer(char *buf, size_t all,
			 size_t row, size_t column, size_t val);

// write_file <sstable/raw> <args>
int main(int argc, char **argv)
{
	LOG4CPLUS_DO_CONFIG("../../conf/log4cplus.tablet_server.conf");
	fs::init("../../conf/gfsclient.conf");

	if(argc <= 1)
	{
		cout << "write_file <sstable/raw> <args>" << endl;
		return 0;
	}

	const string type = argv[1];
	if(type == "sstable")
	{
		if(argc <= 8)
		{
			cout << "write_file sstable <filename> "
				"<row_size> <column_size> <value_size> "
				"<block_size> <entry_number> <compression_code>"
			     << endl;
			return 0;
		}
		const string filename = argv[2];
		const int row_size = ::atoi(argv[3]);
		const int column_size = ::atoi(argv[4]);
		const int value_size = ::atoi(argv[5]);
		const int block_size = ::atoi(argv[6]);
		const int entry_number = ::atoi(argv[7]);
		const int compression_code = ::atoi(argv[8]);
		write_sstable_file(filename, row_size, column_size, value_size,
				   block_size, entry_number, compression_code);
	}
	else if(type == "raw")
	{
		if(argc <= 4)
		{
			cout << "write_file raw <filename> <block_size> <block_number>" << endl;
			return 0;
		}
		const string filename = argv[2];
		const int block_size = ::atoi(argv[3]);
		const int block_number = ::atoi(argv[4]);
		write_raw_file(filename, block_size, block_number);
	}
	else
	{
		cout << "write_file <sstable/raw> <args>" << endl;
	}

	return 0;
}

void write_sstable_file(string filename,
			int row_size,
			int column_size,
			int value_size,
			int block_size,
			int kv_number,
			int code)
{
	TIME_EVALUATOR("writing sstable file ", "");

	size_t size = row_size + column_size + value_size
		+ sizeof(uint32_t) * 3
		+ sizeof(uint64_t);
	size_t vsize = value_size + sizeof(uint32_t);
	char * buf = new char[size];
	fill_sstable_buffer(buf, size,
			    row_size, column_size, value_size);

	const key_wrapper *k = (const key_wrapper*)buf;
	const value_wrapper *v = (const value_wrapper*)(buf + size - vsize);

	sstable_block_cache_manager::instance().init_instance(1024 * 1024);

	sstable::pointer fd = sstable::create(filename,
					      filename,
					      filename,
					      "",
					      "",
					      0,
					      block_size,
					      (compressor::compressor_code)code);
	for(int n = 0; n < kv_number; ++n)
	{
		assert(fd->append(k, v));
	}
	fd->append_eof();

	fd.reset();
	sstable_block_cache_manager::instance().reset_instance();
}

void write_raw_file(string filename, int block_size, int block_number)
{
	TIME_EVALUATOR("writing raw file ", "");

	fs::file_t fd = fs::create(filename);
	if(fd == fs::BAD_FILE)
	{
		cout << "create file failed: " << filename << endl;
		return;
	}
	
	char *buf = new char[block_size];
	fill_raw_buffer(buf, block_size);

	for(int n = 0; n < block_number; ++n)
	{
		assert(fs::writen(fd, buf, block_size) == block_size);
	}

	assert(fs::close(fd));
}

void fill_raw_buffer(char *buf, const size_t size)
{
	size_t s = std::min(sample_data.size(), size);
	memcpy(buf, sample_data.data(), s);
	while(s < size)
	{
		const std::size_t copy_size = std::min(s, size - s);
		memcpy(buf + s, buf, copy_size);
		s += copy_size;
	}
}

void fill_sstable_buffer(char *buf, size_t all,
			 size_t row, size_t column, size_t val)
{
	kb::binary_oarchive<kb::network_byteorder> out(buf, all);

	out << (uint32_t)row;
	fill_raw_buffer(out.left_buffer(), row);
	out.seek(row, SEEK_CUR);

	out << (uint32_t)column;
	fill_raw_buffer(out.left_buffer(), column);
	out.seek(column, SEEK_CUR);

	out << (uint64_t)0;

	out << (uint32_t)val;
	fill_raw_buffer(out.left_buffer(), val);
	out.seek(val, SEEK_CUR);

	assert(out.empty());
}
